/****************************************************************************
 **
 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
 ** All rights reserved.
 ** Contact: Nokia Corporation (qt-info@nokia.com)
 **
 ** This file is part of the examples of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:BSD$
 ** You may use this file under the terms of the BSD license as follows:
 **
 ** "Redistribution and use in source and binary forms, with or without
 ** modification, are permitted provided that the following conditions are
 ** met:
 **   * Redistributions of source code must retain the above copyright
 **     notice, this list of conditions and the following disclaimer.
 **   * Redistributions in binary form must reproduce the above copyright
 **     notice, this list of conditions and the following disclaimer in
 **     the documentation and/or other materials provided with the
 **     distribution.
 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
 **     the names of its contributors may be used to endorse or promote
 **     products derived from this software without specific prior written
 **     permission.
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/

#include <QtGui>
#include <QtXml>

#include "projectitem.h"
#include "projectmanager.h"
#include "itemwizard.h"

ProjectManager::ProjectManager(QDomDocument document, QString path, QObject *parent)
  : QAbstractItemModel(parent), domDocument(document) {
  rootItem = new ProjectItem(domDocument);
  configFile = path;
}

ProjectManager::~ProjectManager() {
  delete rootItem;
}

int ProjectManager::columnCount(const QModelIndex &/*parent*/) const {
  return 3;
}

QString ProjectManager::path(const QModelIndex &index) {
  if (!index.isValid()) {
    return "";
  }

  ProjectItem *item = static_cast<ProjectItem*>(index.internalPointer());
  QDomNode node = item->node();
  QDomNamedNodeMap attributeMap = node.attributes();
  return attributeMap.namedItem("Path").nodeValue();
}

QVariant ProjectManager::data(const QModelIndex &index, int role) const {
  if (!index.isValid()) {
    return QVariant();
  }

  ProjectItem *item = static_cast<ProjectItem*>(index.internalPointer());
  QDomNode node = item->node();
  QDomNamedNodeMap attributeMap = node.attributes();
  QString nodeName = node.nodeName();
  QString path = attributeMap.namedItem("Path").nodeValue();
  QFileInfo f(path);

  if (role == Qt::DisplayRole) {
    switch (index.column()) {
    case 0:

      if (nodeName == "Project") {
        return attributeMap.namedItem("Name").nodeValue();
      } else {
        return f.fileName();
      }

    case 1:
      return nodeName;
    case 2:
      return path;
    default:
      return QVariant();
    }
  } else if (role == Qt::DecorationRole) {
    if (nodeName == "Go") {
        return QIcon(":/icon/source.png");
    } else if (nodeName == "Tmpl") {
      return QIcon(":/icon/template.png");
    } else if (nodeName == "Img") {
      return QIcon(":/icon/image.png");
    } else if (nodeName == "Codes") {
      return QIcon(":/icon/folder_source.png");
    } else if (nodeName == "Statics") {
      return QIcon(":/icon/folder_static.png");
    } else if (nodeName == "Project") {
      return QIcon(":/icon/project.png");
    } else if (nodeName == "Yaml") {
      return QIcon(":/icon/configure.png");
    } else if (nodeName == "Src") {
      return QIcon(":/icon/src.png");
    } else if (nodeName == "Pkg") {
      return QIcon(":/icon/pkg.png");
    } else if (nodeName == "Bin") {
      return QIcon(":/icon/bin.png");
    } else {
      return QIcon(":/icon/text.png");
    }
  }

  return QVariant();
}

Qt::ItemFlags ProjectManager::flags(const QModelIndex &index) const {
  if (!index.isValid()) {
    return 0;
  }

  return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

QVariant ProjectManager::headerData(int section, Qt::Orientation orientation,
                                    int role) const {
  if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
    switch (section) {
    case 0:
      return "Name";
    case 1:
      return "Type";
    case 2:
      return "Path";
    }
  }

  return QVariant();
}

QModelIndex ProjectManager::index(int row, int column, const QModelIndex &parent)
const {
  if (!hasIndex(row, column, parent)) {
    return QModelIndex();
  }

  ProjectItem *parentItem;

  if (!parent.isValid()) {
    parentItem = rootItem;
  } else {
    parentItem = static_cast<ProjectItem*>(parent.internalPointer());
  }

  ProjectItem *childItem = parentItem->child(row);

  if (childItem) {
    return createIndex(row, column, childItem);
  } else {
    return QModelIndex();
  }
}

QModelIndex ProjectManager::parent(const QModelIndex &child) const {
  if (!child.isValid()) {
    return QModelIndex();
  }

  ProjectItem *childItem = static_cast<ProjectItem*>(child.internalPointer());
  ProjectItem *parentItem = childItem->parent();

  if (!parentItem || parentItem == rootItem) {
    return QModelIndex();
  }

  return createIndex(parentItem->row(), 0, parentItem);
}

int ProjectManager::rowCount(const QModelIndex &parent) const {
  if (parent.column() > 0) {
    return 0;
  }

  ProjectItem *parentItem;

  if (!parent.isValid()) {
    parentItem = rootItem;
  } else {
    parentItem = static_cast<ProjectItem*>(parent.internalPointer());
  }

  return parentItem->node().childNodes().count();
}
ProjectItem* ProjectManager::getItem(const QModelIndex &index) const {
  if (index.isValid()) {
    ProjectItem* item = static_cast<ProjectItem*>(index.internalPointer());

    if (item) {
      return item;
    }
  }

  return rootItem;
}

bool ProjectManager::setData(const QModelIndex &index, const QVariant &value, int role) {
  if (role != Qt::EditRole) {
    return false;
  }

  ProjectItem* item = getItem(index);
  bool result = item->setData(index.column(), value);

  if (result) {
    emit dataChanged(index, index);
  }

  return result;
}

bool ProjectManager::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) {
  if (role != Qt::EditRole || orientation != Qt::Horizontal) {
    return false;
  }

  bool result = rootItem->setData(section, value);

  if (result) {
    emit headerDataChanged(orientation, section, section);
  }

  return result;
}

bool ProjectManager::insertRows(int row, int count, const QModelIndex &parent) {
  if (row < 0 || row > rowCount(parent) || !parent.isValid()) {
    return false;
  }
  ProjectItem *parentItem = getItem(parent);
  ItemWizard w;
  QString nodeName = parentItem->node().nodeName();
  if(nodeName == "Codes") {
    w.disable(1);
    w.disable(2);
    w.disable(3);
    w.disable(4);
  }
  if (w.exec()) {
    beginInsertRows(parent, row, row + count - 1);
    int type = w.field("filetype").toInt();
    QString tag;
    QString name = w.field("filename").toString();
    if(nodeName == "Codes" || nodeName == "Statics" || nodeName == "Src") {
      name.prepend(parentItem->node().attributes()
                   .namedItem("Path").nodeValue().append("/"));
    }

    switch (type) {
      case ItemWizard::Go_Page:
        tag = "Go";
        name = name.append(".go");
        break;
      case ItemWizard::Codes_Page:
        tag = "Codes";
        break;
      case ItemWizard::Statics_Page:
        tag = "Statics";
        break;
      case ItemWizard::Template_Page:
        tag = "Tmpl";
        name = name.append(".html");
        break;
      default:
        tag = "File";
        break;
    }
    QFileInfo cfg(configFile);
    QDir projectDir = cfg.absoluteDir();
    if(tag == "Codes" || tag == "Statics") {
      QDir dir(projectDir.filePath(name));
      if (!dir.exists()) {
          dir.mkpath(".");
      } else {
        QString err("Folder \"%1\" already exists!");
        QMessageBox::warning(0, "Cannot create folder", err.arg(name));
        return false;
      }
    } else {
      QFile file(projectDir.filePath(name));
      if(file.exists()) {
        QString err("The \"%1\" already exists!");
        QMessageBox::warning(0, "Cannot create file", err.arg(name));
        return false;
      }
      file.open(QIODevice::WriteOnly);
      if(tag == "Go") {
        file.write(QString("package ").toUtf8());
      }
      file.close();
    }

    QDomDocument document;
    QDomElement element = document.createElement(tag);
    element.setAttribute("Path", name);

    parentItem->appendChildren(element);
    endInsertRows();
    return updateConfig();
  }
  return false;
}

bool ProjectManager::removeRows(int row, int count, const QModelIndex &parent)
{
  if (row < 0 || !parent.isValid()) {
    return false;
  }
  ProjectItem *parentItem = getItem(parent);
  beginRemoveRows(parent, row, row + count - 1);
  parentItem->removeChildren(row);
  updateConfig();
  endRemoveRows();
  return true;
}

bool ProjectManager::updateConfig()
{
  QFile f(configFile);
  if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
    f.write(domDocument.toByteArray());
    return true;
  }
  return false;
}

int ProjectManager::kind() {
  if(rootItem->node().firstChild().attributes().namedItem("Type").nodeValue()
     == "GAE") {
    QSettings s;
    if(s.value("general/appengine/path").toString().isEmpty()) {
      QMessageBox::warning(0, "Missing necessary configuration",
                           QString("This is an Google Go App Engine project ")
                           .append("but the App Engine Path has not been set."));
    }
    return GAE;
  } else {
    return NAV;
  }
}

QStringList ProjectManager::codeFiles() {
  QStringList filenames;
  QDomNodeList srcs = rootItem->node().toDocument().elementsByTagName("Src");
  if(srcs.length() > 0) {
    QDomNodeList codesList = srcs.at(0).toElement().elementsByTagName("Codes");
    for(uint i = 0; i < codesList.length(); i++) {
      QDomNodeList goList = codesList.at(i).toElement().elementsByTagName("Go");
      for(uint j = 0; j < goList.length(); j++) {
        filenames.append(goList.at(j).attributes().namedItem("Path").nodeValue());
      }
    }

  }
  return filenames;
}
